extern crate dirs;
extern crate toml;

// use std::time::SystemTime;
// use std::path::Path;
use std::io::Read;
use std::io::Write;
// use std::io::prelude::*;
use std::fs;
use std::fs::File;
use toml::Value as Toml;

// use serde_json::Value as Json;
// use serde_derive::{Deserialize};
use super::utils::OutErr;
use log::info;
use std::collections::HashMap;
use std::path::Path;

use uuid::Uuid;

const DEFAUT_CONFIG_TEMPLATE: &str = r#"
[base]
dir_name = "~/.mu-cache"
password = "fuck you world!"
# [R] is mean root of '~/.mu-cache'

[gits]
sqlmap = "--depth 1 https://github.com/sqlmapproject/sqlmap.git ~/.mu-cache/sqlmap"
dirmap = "https://github.com/H4ckForJob/dirmap.git ~/.mu-cache/dirmap && cd ~/.mu-cache/dirmap && python3 -m [PIP] install -r requirement.txt"
[pys]
[apt]

[plugins]
dbs = "[PY] [R]/sqlmap/sqlmap.py --delay=1 --timeout=15 --retries=2 --keep-alive --threads=5 --eta --answer yes --random-agent  -u '[T]' --dbs "

db = "[PY] [R]/sqlmap/sqlmap.py --delay=1 --timeout=15 --retries=2 --keep-alive --threads=5 --eta --answer yes --random-agent  -u '[T]' "

c_range = "cd [R]/dirmap/ && python3 dirmap.py -i [T]/24 -lcf"

web = "cd [R]/dirmap/ && python3 dirmap.py -i [T] -lcf"

test = "curl [T] " 
[test]
db = "[PY] [R]/sqlmap/sqlmap.py -h"
web = "cd [R]/dirmap/ && python3 dirmap.py -h"
"#;

fn _f(p: &str) -> String {
    if p.contains("\"") {
        p.replace("\"", "")
    } else {
        p.to_string()
    }
}

const BASE_DIR: &str = ".mu-cache";
// const DEFAULT_CONFIG:&str = ".mu-cache/config.toml";

pub fn home(get_config: bool) -> String {
    if get_config {
        dirs::home_dir()
            .expect("no home dir")
            .as_path()
            .join(BASE_DIR)
            .join("config.toml")
            .to_str()
            .unwrap()
            .to_string()
    } else {
        dirs::home_dir()
            .expect("no home dir")
            .as_path()
            .join(BASE_DIR)
            .to_str()
            .unwrap()
            .to_string()
    }
}

fn write_default_config() -> OutErr<()> {
    let h_dir = home(false);
    let h = home(true);
    let p = Path::new(&h_dir);
    if !p.exists() {
        info!("try to crate base home dir{}", h_dir);
        fs::create_dir_all(p).expect("create dir error ");
    }
    if !Path::new(&h).exists() {
        let mut f = File::create(&h)?;
        f.write_all(DEFAUT_CONFIG_TEMPLATE.as_bytes())
            .expect("write to config error");
    }

    Ok(())
}

pub fn read_config() -> OutErr<Toml> {
    let h = home(true);
    let mut buf = String::new();
    if !Path::new(&h).exists() {
        write_default_config().expect("write default config error");
    }

    let output_path = Path::new(&home(false)).join("[OUPUT]");
    if !output_path.exists() {
        fs::create_dir_all(&output_path).expect("create output dir failed!");
    }

    let mut file = File::open(&h).expect("no such config");
    file.read_to_string(&mut buf)
        .expect("read config string error");
    let value = buf.parse::<Toml>().expect("parse config string error");
    // let value = toml::Parser::new(buf)?;
    Ok(value)
}

pub fn get_sub_map(name: &str) -> HashMap<String, String> {
    let c = read_config().expect("can not read config");
    let mut map: HashMap<String, String> = HashMap::new();
    match &c[name] {
        Toml::Table(s) => {
            let _ = s
                .into_iter()
                .map(|(k, v)| {
                    map.insert(k.to_string(), v.as_str().unwrap().to_string());
                })
                .collect::<Vec<()>>();
        }
        _ => println!("{} ", "err valid"),
    }
    map
}

pub fn get_base_config() -> HashMap<String, String> {
    get_sub_map("base")
}

pub fn get_config_password() -> String {
    if let Some(pwd) = get_base_config().get("password") {
        return pwd.to_string();
    } else {
        return "fuck you world!".to_string();
    }
}

fn check_path(name: &str) -> String {
    // let h = home(false);
    // if Path::new(&h).join(name).exists(){
    //     return Path::new(&h).join(name).as_path().to_str().unwrap().to_string();
    // }
    for path_str in vec![
        "/bin/",
        "/usr/bin/",
        "/usr/local/bin/",
        "/sbin/",
        "/usr/sbin/",
    ] {
        if Path::new(path_str).join(name).exists() {
            return Path::new(path_str).join(name).to_str().unwrap().to_string();
        }
    }

    "".to_string()
}

pub fn get_plugin(name: &str, version: Option<&str>) -> String {
    let map = get_sub_map("plugins");
    let mut cmd = if map.contains_key(name) {
        match map.get(name) {
            Some(i) => i.replace("[R]", &home(false)),
            _ => "".to_string(),
        }
    } else {
        check_path(name)
    };
    if let Some(version) = version {
        if cmd.contains("[PY]") {
            cmd = cmd.replace("[PY]", version)
        }
    }
    cmd
}

pub fn get_test(name: &str) -> String {
    let map = get_sub_map("test");

    if map.contains_key(name) {
        match map.get(name) {
            Some(i) => i.replace("[R]", &home(false)),
            _ => "".to_string(),
        }
    } else {
        let mut out = check_path(name);
        out.push_str(" -h");
        out.clone()
    }
}

pub fn home_output() -> String {
    let output_path = Path::new(&home(false)).join("[OUPUT]");
    if !output_path.exists() {
        fs::create_dir_all(&output_path).expect("create output dir failed!");
    }
    output_path.as_path().to_str().unwrap().to_string()
}

pub fn clear_home_output() -> OutErr<String> {
    let h = home_output();
    let mut buf = String::new();
    for p in fs::read_dir(h)? {
        let path = p?.path();
        if path.is_dir() {
            continue;
        }
        let id_str = path.file_name().expect("no file name ").to_str().unwrap();
        match Uuid::parse_str(id_str) {
            Ok(_) => {
                buf.push_str(id_str);
                buf.push_str("\n");
                fs::remove_file(path)?;
            }
            Err(_) => {}
        }
    }
    Ok(buf)
}
// fn convert(toml: Toml) -> Json {
//     match toml {
//         Toml::String(s) => Json::String(s),
//         Toml::Integer(i) => Json::Number(i.into()),
//         Toml::Float(f) => {
//             let n = serde_json::Number::from_f64(f).expect("float infinite and nan not allowed");
//             Json::Number(n)
//         }
//         Toml::Boolean(b) => Json::Bool(b),
//         Toml::Array(arr) => Json::Array(arr.into_iter().map(convert).collect()),
//         Toml::Table(table) => {
//             Json::Object(table.into_iter().map(|(k, v)| (k, convert(v))).collect())
//         }
//         Toml::Datetime(dt) => Json::String(dt.to_string()),
//     }
// }
